#include "GameObject3D.h"
#include <assimp/Importer.hpp> // C++ importer interface
#include <assimp/scene.h> // Output data structure
#include <assimp/postprocess.h> // Post processing flags

void GameObject3D::Init()
{

}
void GameObject3D::Render()
{
	/*
	for (int i = 0; i < meshList.size(); i++)
	{
		Device_Base::GetInstance()->BindVBO(meshList[i]->vbo);
		Device_Base::GetInstance()->BindVBO(meshList[i]->indexVBO);
		Device_Base::GetInstance()->UpdateVertexAttribPointer();
		Device_Base::GetInstance()->SetUniformMatrix(UNIFORM_MATRIX_MODEL, Device_Base::GetInstance()->GetMatrixWorld());
		Device_Base::GetInstance()->SetUniformMatrix(UNIFORM_MATRIX_VIEW, Device_Base::GetInstance()->GetMatrixView());
		Device_Base::GetInstance()->SetUniformMatrix(UNIFORM_MATRIX_PROJECTION, Device_Base::GetInstance()->GetMatrixProjection());
		Device_Base::GetInstance()->SetUniformTexture(UNIFORM_TEXTURE_DIFFUSE, meshList[i]->material->diffuseTexture);
		Device_Base::GetInstance()->DrawElement(Device_Base::PRIMITIVE_TRIANGLE_LIST, meshList[i]->indexVBO->vertexCount, NULL);
	}
	*/
	RenderNode(&rootNode);
}
void GameObject3D::RenderNode(Node* node)
{
	Device_Base::GetInstance()->PushMatrix();
	Device_Base::GetInstance()->Transform(node->transformMatrix);
	for (int i = 0; i < node->meshes.size(); i++)
	{
		Device_Base::GetInstance()->BindVBO(node->meshes[i]->vbo);
		Device_Base::GetInstance()->BindVBO(node->meshes[i]->indexVBO);
		Device_Base::GetInstance()->UpdateVertexAttribPointer();
		Device_Base::GetInstance()->SetUniformMatrix(UNIFORM_MATRIX_MODEL, Device_Base::GetInstance()->GetMatrixWorld());
		Device_Base::GetInstance()->SetUniformMatrix(UNIFORM_MATRIX_VIEW, Device_Base::GetInstance()->GetMatrixView());
		Device_Base::GetInstance()->SetUniformMatrix(UNIFORM_MATRIX_PROJECTION, Device_Base::GetInstance()->GetMatrixProjection());
		Device_Base::GetInstance()->SetUniformTexture(UNIFORM_TEXTURE_DIFFUSE, node->meshes[i]->material->diffuseTexture);
		Device_Base::GetInstance()->DrawElement(Device_Base::PRIMITIVE_TRIANGLE_LIST, node->meshes[i]->indexVBO->vertexCount, NULL);
	}
	for (int i = 0; i < node->childNodes.size(); i++)
	{
		RenderNode(node->childNodes[i]);
	}
	Device_Base::GetInstance()->PopMatrix();
}
void GameObject3D::Update(float timeDelta)
{

}
void GameObject3D::Release()
{
	for (int i = 0; i < meshList.size(); i++)
	{
		SafeDelete(meshList[i]);
	}
	meshList.clear();
}

void GameObject3D::LoadFromFile(const char* filePath)
{
	// Create an instance of the Importer class
	Assimp::Importer importer;
	// And have it read the given file with some example postprocessing
	// Usually - if speed is not the most important aspect for you - you'll
	// propably to request more postprocessing than we do in this example.
 
	const aiScene* scene = importer.ReadFile(filePath,
	//	aiProcess_CalcTangentSpace |
		aiProcess_Triangulate |
		//aiProcess_JoinIdenticalVertices |
		aiProcess_SortByPType);
	// If the import failed, report it
	if (!scene)
	{
		DebugLog(importer.GetErrorString());
		return;
	}
	for (int i = 0; i < scene->mNumMeshes; i++)
	{
		Mesh* mesh = new Mesh();
		mesh->vbo = new VBO(VBO::VBO_TARGET_ARRAY_BUFFER);
		mesh->vbo->usage = VBO::VBO_USAGE_STATIC_DRAW;
		//vboList[i]->SetAttributeOffset(ATTRIBUTE_FLOAT2_TEXCOORD_DIFFUSE, 3 * sizeof(float));
		aiMesh* aimesh = scene->mMeshes[i];

		int vertexSizeInByte = 0;// vboVerticesCount * sizeof(float) * 5; //x, y, z, u, v
		
		if (aimesh->HasPositions())
		{
			if (aimesh->mPrimitiveTypes & aiPrimitiveType_TRIANGLE)
			{
				mesh->vbo->SetAttributeOffset(ATTRIBUTE_FLOAT3_POSITION, vertexSizeInByte);
				vertexSizeInByte += sizeof(float) * 3;
			}
		}
		if (aimesh->HasTextureCoords(0))
		{
			mesh->vbo->SetAttributeOffset(ATTRIBUTE_FLOAT2_TEXCOORD_DIFFUSE, vertexSizeInByte);
			vertexSizeInByte += sizeof(float) * 2;
		}
		int vboSizeInByte = vertexSizeInByte * aimesh->mNumVertices;
		char* vboData = new char[vboSizeInByte]; //Not support seperated VBO at this time
		
		if (aimesh->HasPositions())
		{
			if (aimesh->mPrimitiveTypes & aiPrimitiveType_TRIANGLE)
			{
				int index = mesh->vbo->attributeMap[ATTRIBUTE_FLOAT3_POSITION].id;
				for (int j = 0; j < aimesh->mNumVertices; j++)
				{
					memcpy(vboData + index + vertexSizeInByte * j, aimesh->mVertices + j, sizeof(float) * 3);
				}
			}
		}
		if (aimesh->HasTextureCoords(0))
		{
			int index = mesh->vbo->attributeMap[ATTRIBUTE_FLOAT2_TEXCOORD_DIFFUSE].id;
			for (int j = 0; j < aimesh->mNumVertices; j++)
			{
				memcpy(vboData + index + vertexSizeInByte * j, aimesh->mTextureCoords[0] + j, sizeof(float) * 2);
			}
		}
		Device_Base::GetInstance()->CreateVBO(mesh->vbo, vboData, vboSizeInByte);
		SafeDeleteArray(vboData);
		if (aimesh->HasFaces()) //triangle preprocess
		{
			mesh->indexVBO = new VBO(VBO::VBO_TARGET_ELEMENT_ARRAY_BUFFER);
			mesh->indexVBO->usage = VBO::VBO_USAGE_STATIC_DRAW;
			unsigned int* indexData = new unsigned int[aimesh->mNumFaces * 3];
			for (int j = 0; j < aimesh->mNumFaces; j++)
			{
				memcpy(indexData + j * 3, aimesh->mFaces[j].mIndices, sizeof(unsigned int) * 3);
			}
			Device_Base::GetInstance()->CreateVBO(mesh->indexVBO, (char*)indexData, sizeof(unsigned int) * aimesh->mNumFaces * 3);
			SafeDeleteArray(indexData);
		}
		else
		{
			mesh->indexVBO = NULL;
		}
		mesh->material = new Material();
		aiMaterial* aimat = scene->mMaterials[aimesh->mMaterialIndex];
		aiString name;
		aiGetMaterialString(aimat, AI_MATKEY_NAME, &name);
		aiString diffuseTextureName;
		aimat->GetTexture(aiTextureType_DIFFUSE, 0, &diffuseTextureName);
		mesh->material->diffuseTexture = TextureManager::GetInstance()->LoadTexture(diffuseTextureName.C_Str());
		meshList.push_back(mesh);
	}
	rootNode.InitFromAssimpNode(scene->mRootNode, meshList);
}